package com.demo.function.arithmetic.timewheel;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.DelayQueue;

@Slf4j
public class TimeWheel {
    /**
     * 一个槽的时间间隔(时间轮最小刻度)
     */
    private final long tickMs;

    /**
     * 时间轮大小(槽的个数)
     */
    private final int wheelSize;

    /**
     * 一轮的时间跨度
     */
    private final long interval;

    private long currentTime;

    /**
     * 槽
     */
    private final TimerTaskList[] buckets;

    /**
     * 上层时间轮
     */
    private volatile TimeWheel overflowWheel;

    /**
     * 一个timer只有一个delayqueue
     */
    private final DelayQueue<TimerTaskList> delayQueue;

    public TimeWheel(long tickMs, int wheelSize, long currentTime, DelayQueue<TimerTaskList> delayQueue) {
        this.tickMs = tickMs;
        this.wheelSize = wheelSize;
        this.interval = tickMs * wheelSize;
        this.buckets = new TimerTaskList[wheelSize];
        // 起始时间(currentTime)都设置为创建此层时间轮时前面一轮的currentTime，每一层的currentTime都必须是tickMs的整数倍
        this.currentTime = currentTime - (currentTime % tickMs);
        this.delayQueue = delayQueue;
        for (int i = 0; i < wheelSize; i++) {
            buckets[i] = new TimerTaskList();
        }
    }

    /**
     * 添加任务
     * @param entry
     * @return
     */
    public boolean add(TimerTaskEntry entry) {
        //获取任务延迟时间
        long expiration = entry.getExpireMs();
        if (expiration < tickMs + currentTime) {
            // 到期了
            return false;
        } else if (expiration < currentTime + interval) {
            //扔进当前时间轮的某个槽里,只有时间大于某个槽,才会放进去
            long virtualId = (expiration / tickMs);
            //计算槽位
            int index = (int) (virtualId % wheelSize);
            log.info("desc:{}, 槽位:{}", entry.getTimerTask().getDesc(), index );
            TimerTaskList bucket = buckets[index];
            //将任务添加到槽位链表中
            bucket.addTask(entry);
            //设置bucket 过期时间
            if (bucket.setExpiration(virtualId * tickMs)) {
                //设好过期时间的bucket需要入队
                delayQueue.offer(bucket);
            }
            return true;
        } else {
            //当前轮不能满足,需要扔到上一轮，递归处理，直至添加成功
            TimeWheel timeWheel = getOverflowWheel();
            return timeWheel.add(entry);
        }
    }

    /**
     * 当时间溢出时，需要新增上一级时间轮，通过overflowWheel引用上一级侧时间轮
     * @return 返回上层时间轮对象
     */
    private TimeWheel getOverflowWheel() {
        if (overflowWheel == null) {
            synchronized (this) {
                if (overflowWheel == null) {
                    overflowWheel = new TimeWheel(interval, wheelSize, currentTime, delayQueue);
                }
            }
        }
        return overflowWheel;
    }

    /**
     * 推进指针
     *
     * @param expireTime 过期时间
     */
    public void advanceLock(long expireTime) {
        log.info("expireTime:{}, currentTime:{}, currentTime + tickMs:{}", expireTime, currentTime, currentTime + tickMs);
        if (expireTime >= currentTime + tickMs) {
            //调整当前时间
            currentTime = expireTime - (expireTime % tickMs);
            if (overflowWheel != null) {
                //递归推进上级时间轮
                this.getOverflowWheel().advanceLock(expireTime);
            }
        }
    }
}
